package fwmpc.utils;

import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.digest.DigestUtils;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import javax.xml.XMLConstants;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.io.StringWriter;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.*;

/**
 * 类描述:  微信工具
 *
 * @author 王雷
 * @version 1.0.0
 * @date 2022/7/5 0005 下午 1:55
 */
@Slf4j
public class WeiXinToolUtil {

    protected static final char[] BASE_CHARACTER = "abcdefghijklmnopqrstuvwxyz0123456789".toCharArray();

    private static final WeiXinToolUtil WEI_XIN_TOOL_UTIL = new WeiXinToolUtil();

    /**
     * 功能描述：微信工具工厂
     *
     * @date : 2022/7/5 0005 下午 2:43
     */
    public static WeiXinToolUtil factory() {
        return WEI_XIN_TOOL_UTIL;
    }

    /**
     * 功能描述：创建时间戳
     *
     * @date : 2022/7/18 0018 下午 9:44
     */
    public String creationTimestamp() {
        return Long.toString(System.currentTimeMillis() / 1000);
    }

    /**
     * 功能描述：获取随机位数的字符串
     *
     * @date : 2022/7/5 0005 下午 1:56
     */
    public String giveRandomString(int length) {
        Random random = new Random();
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < length; i++) {
            sb.append(BASE_CHARACTER[random.nextInt(BASE_CHARACTER.length)]);
        }
        return sb.toString();
    }

    /**
     * 功能描述：获得微信签名方法
     *
     * @param managerData   需要处理的数据
     * @param randomKey     随机字符串
     * @param encryptionKey 加密Key
     * @param expiration    失效时间；单位：秒
     * @param secret        secret标识
     * @date : 2022/6/29 0029 下午 8:15
     */
    public <D> String generateToken(D managerData, String randomKey, String encryptionKey, Long expiration, String secret) throws Exception {
        Map<String, Object> claims = new HashMap<>(16);
        claims.put(encryptionKey, randomKey);
        return doGenerateToken(claims, managerData, expiration, secret);
    }

    /**
     * 功能描述：map转成xml
     *
     * @date : 2022/6/29 0029 下午 7:56
     */
    public String mapToXmlWith(Map<String, Object> data) throws Exception {
        Document document = newDocument();
        Element root = document.createElement("xml");
        document.appendChild(root);
        for (String key : data.keySet()) {
            String value = data.get(key).toString();
            if (value == null) {
                value = "";
            }
            value = value.trim();
            Element filed = document.createElement(key);
            filed.appendChild(document.createTextNode(value));
            root.appendChild(filed);
        }
        TransformerFactory tf = TransformerFactory.newInstance();
        Transformer transformer = tf.newTransformer();
        DOMSource source = new DOMSource(document);
        transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
        transformer.setOutputProperty(OutputKeys.INDENT, "yes");
        StringWriter writer = new StringWriter();
        StreamResult result = new StreamResult(writer);
        transformer.transform(source, result);
        String output = writer.getBuffer().toString();
        try {
            writer.close();
        } catch (Exception ex) {
            log.error("writer关闭失败", ex);
        }
        return output;
    }

    protected DocumentBuilder newDocumentBuilder() throws ParserConfigurationException {
        DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
        documentBuilderFactory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
        documentBuilderFactory.setFeature("http://xml.org/sax/features/external-general-entities", false);
        documentBuilderFactory.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
        documentBuilderFactory.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
        documentBuilderFactory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
        documentBuilderFactory.setXIncludeAware(false);
        documentBuilderFactory.setExpandEntityReferences(false);

        return documentBuilderFactory.newDocumentBuilder();
    }

    protected Document newDocument() throws ParserConfigurationException {
        return newDocumentBuilder().newDocument();
    }

    /**
     * 功能描述：字符串转XML
     *
     * @param xml 需要转换的字符串
     * @date : 2022/7/5 0005 下午 2:01
     */
    public Map<String, String> strToXmlParse(String xml) throws Exception {
        Map<String, String> data = new HashMap<>(16);
        DocumentBuilder documentBuilder = newDocumentBuilder();
        InputStream stream = new ByteArrayInputStream(xml.getBytes(StandardCharsets.UTF_8));
        Document doc = documentBuilder.parse(stream);
        doc.getDocumentElement().normalize();
        NodeList nodeList = doc.getDocumentElement().getChildNodes();
        for (int idx = 0; idx < nodeList.getLength(); ++idx) {
            Node node = nodeList.item(idx);
            if (node.getNodeType() == Node.ELEMENT_NODE) {
                Element element = (Element) node;
                data.put(element.getNodeName(), element.getTextContent());
            }
        }
        try {
            stream.close();
        } catch (Exception ex) {
            // do nothing
            log.error("关闭stream异常", ex);
        }
        return data;
    }

    /**
     * 功能描述：XML文件流转Map
     *
     * @param inputStream 流对象
     * @date : 2022/7/23 0023 上午 9:30
     */
    public Map<String, String> inputStreamXmlFileToMap(InputStream inputStream) throws Exception {
        Map<String, String> data = new HashMap<>(16);
        DocumentBuilder documentBuilder = newDocumentBuilder();
        Document doc = documentBuilder.parse(inputStream);
        doc.getDocumentElement().normalize();
        NodeList nodeList = doc.getDocumentElement().getChildNodes();
        for (int idx = 0; idx < nodeList.getLength(); ++idx) {
            Node node = nodeList.item(idx);
            if (node.getNodeType() == Node.ELEMENT_NODE) {
                Element element = (Element) node;
                data.put(element.getNodeName(), element.getTextContent());
            }
        }
        inputStream.close();
        return data;
    }

    /**
     * 功能描述：生成token
     *
     * @param claims      需要处理的容器
     * @param managerData 需要处理的数据
     * @param expiration  失效时间；单位：秒
     * @param secret      secret标识
     * @date : 2022/7/5 0005 下午 3:24
     */
    protected <D> String doGenerateToken(Map<String, Object> claims, D managerData, Long expiration, String secret) throws Exception {

        final Date createdDate = new Date();
        //失效时间
        final Date expirationDate = Date.from(LocalDateTime.now().plusSeconds(expiration).atZone(ZoneId.systemDefault())
                .toInstant());

        log.debug("当前处理的后的时间——createdDate：{}；expirationDate：{}", createdDate, expirationDate);

        return Jwts.builder().setClaims(claims).setSubject(managerData.toString()).setIssuedAt(createdDate)
                .setExpiration(expirationDate).signWith(SignatureAlgorithm.HS512, secret).compact();
    }

    private final static String CHARS_DATA = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";

    /**
     * 功能描述：创建Nonces信息
     *
     * @date : 2022/6/29 0029 下午 7:39
     */
    public String giveCreateNonceStr() {
        StringBuilder res = new StringBuilder();
        for (int i = 0; i < 16; i++) {
            Random rd = new Random();
            res.append(CHARS_DATA.charAt(rd.nextInt(CHARS_DATA.length() - 1)));
        }
        return res.toString();
    }

    /**
     * 功能描述：数据签名处理
     *
     * @param params 需要签名的内容
     * @param appkey 签名标识
     * @return 签名结果
     * @date : 2022/7/19 0019 下午 2:46
     */
    public String dataSign(Map<String, Object> params, String appkey) {
        Set<String> keysSet = params.keySet();
        String[] keys = keysSet.toArray(new String[0]);
        Arrays.sort(keys);
        StringBuilder temp = new StringBuilder();
        boolean first = true;
        for (String key : keys) {
            if (first) {
                first = false;
            } else {
                temp.append("&");
            }
            temp.append(key).append("=");
            Object value = params.get(key);
            String valueString = "";
            if (null != value) {
                valueString = value.toString();
            }
            temp.append(valueString);
        }
        String stringSignTemp = temp.toString() + "&key=" + appkey;
        log.info("stringSignTemp的结果为：{}", stringSignTemp);
        return DigestUtils.md5Hex(stringSignTemp).toUpperCase();
    }
}
